package com.ymx.darling.expression;

import com.ymx.darling.bean.entity.BeanInfoManager;
import com.ymx.darling.bean.entity.FieldInfo;
import com.ymx.darling.bean.sql.Parameter;
import com.ymx.darling.build.mapper.dynamic.DynamicContext;
import com.ymx.darling.exception.ExceptionFactory;
import com.ymx.darling.exception.ExpressionException;
import com.ymx.darling.exception.ParameterNotFoundException;
import com.ymx.darling.type.EnumTypeHandler;
import com.ymx.darling.type.TypeHandler;
import com.ymx.darling.util.ObjectUtil;
import com.ymx.darling.util.ReflectUtil;
import com.ymx.darling.util.StringUtil;

import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**t
 * 表达式访问处理器
 * @author 爱Java的小于
 */
public class ExpressionAccess {
    /**
     * 保存上下文信息
     */
    private final Object context;
    /**
     *  保存指定set集合的索引元素
     */
    private final Map<Collection<?>, Object[]> collectionvaluemap = new HashMap<>();

    /**
     * 表达式访问链的缓存
     */
    private static final Map<String, LinkedList<String>> expressionLinkedCache = new ConcurrentHashMap<>();

    /**
     * 构造函数
     * @param context 上下文实例
     */
    public ExpressionAccess(Object context) {
        this.context = context;
    }


    /**t
     * 根据表达式访问实体中的数据并返回
     * @param accessExpress 访问表达式用'.'连接数组使用[]
     * @return 返回值
     * @throws com.ymx.darling.exception.EntityException 可能会出现访问错误将抛出此异常
     */
    public Object getValue(String accessExpress) {
        if (StringUtil.isNull(accessExpress)) {
            return this.context;
        } else {
            // 将访问表达式解析成访问链表
            LinkedList<String> linkedAccess = parseExpress(accessExpress);
            // 判断当前上下文为null时存在访问链表抛出异常
            if (this.context == null) {
                if (linkedAccess.size() > 1) {
                    throw new ExpressionException(String.format("表达式%s访问异常.原因:%s的值为null", accessExpress, linkedAccess.removeFirst()));
                }
                return null;
            }
            // 当类型为简单类型时 将表达式的开头去除
            if (ObjectUtil.isSimpleType(this.context.getClass()) && !linkedAccess.isEmpty()) {
                linkedAccess.removeFirst();
            }
            try {
                return getValue(this.context, linkedAccess);
            } catch (Exception exception) {
                // 防止出现表达式解析异常重复包装
                if (exception instanceof ExpressionException) {
                    throw (ExpressionException)exception;
                } else {
                    String errMsg = String.format("表达式或字段'%s'访问异常.原因:%s:%s", accessExpress, exception.getClass().getName(), exception.getMessage());
                    throw ExceptionFactory.throwStrategy(exception, new ExpressionException(exception, errMsg));
                }
            }
        }
    }


    /**
     * 获取指定的值并返回
     * @param obj 被应用的实体
     * @param accessLinked 访问表达式链
     * @return Object
     * @throws Exception 代替所有异常返回
     */
    private Object getValue(Object obj, LinkedList<String> accessLinked) throws Exception {
        if (accessLinked.isEmpty()) {
            return wrapValueIfEnum(obj);
        } else {
            String placeholder = accessLinked.removeFirst();
            Object paramEntity = unwrapValue(obj);
            Object value;
            // 判断map类型
            if (paramEntity instanceof Map) {
                value = this.parseMapValue((Map<?, ?>) paramEntity, placeholder);
            // 判断list类型
            } else if (paramEntity instanceof List) {
                value = this.parseListValue((List<?>) paramEntity, placeholder);
            // 判断Collection类型
            } else if (paramEntity instanceof Collection) {
                value = this.parseCollectionValue((Collection<?>) paramEntity, placeholder);
            // 判断数组
            } else if (paramEntity.getClass().isArray()) {
                value = this.parseArrayValue(paramEntity, placeholder);
            // 判断被注册的@Entity注解的实体类
            } else if (BeanInfoManager.isRegister(paramEntity.getClass())) {
                value = this.parseEntityValue(paramEntity, placeholder);
            // 其他类型就是用反射机制获取
            } else {
                value = this.parseOtherTypeValue(paramEntity, placeholder);
            }
            // 判断被引用的实体为null的情况下抛出异常
            if (value == null && !accessLinked.isEmpty()) {
                throw new NullPointerException("'" + placeholder + "'的值为null");
            }
            return this.getValue(value, accessLinked);
        }
    }


    /**
     * 解析map中的值
     * @param map map
     * @param placeholder 参数引用名称
     * @return Object
     * @throws NoSuchMethodException 找不到指定方法时抛出此异常
     */
    private Object parseMapValue(Map<?,?> map, String placeholder) throws NoSuchMethodException {
        // 判断引用的上下文的实例,那么只能获取的key:value的值
        if (this.isContext(map)) {
            if (!map.containsKey(placeholder)) {
                // 需要对DynamicContext特殊处理 因为其内部可能保存其他类型的上下文(常见于动态sql上下文)
                if (map instanceof DynamicContext) {
                    DynamicContext dynamicContext = (DynamicContext) map;
                    if (dynamicContext.isEmpty() && !dynamicContext.isContext()) {
                        return map.get(placeholder);
                    }
                }
                throw new ParameterNotFoundException(String.format("参数列表%s不存在名为'%s'的参数信息", map.keySet(), placeholder));
            }
            return map.get(placeholder);
        } else {
            // map为空时直接返回null
            if (map.isEmpty()) {
                return null;
            }
            // 判断开头为[那么表示引用的是key的值
            if (this.checkBracketed(placeholder)) {
                String key = this.parseBracketContent(placeholder);
                Class<?> keyType = map.keySet().iterator().next().getClass();
                Object keyValue = ReflectUtil.convertBasicType(keyType, key);
                return map.get(keyValue);
            }
            // 返回map中方法
            return this.parseOtherTypeValue(map, placeholder);
        }
    }


    /**
     * 解析List中的值
     * @param list list
     * @param placeholder 参数引用名称
     * @return Object
     * @throws NoSuchMethodException 找不到指定方法时抛出此异常
     */
    private Object parseListValue(List<?> list, String placeholder) throws NoSuchMethodException {
        if (this.checkBracketed(placeholder)) {
            String indexString = this.parseBracketContent(placeholder);
            return list.get(Integer.parseInt(indexString));
        }
        return this.parseOtherTypeValue(list, placeholder);
    }


    /**
     * 解析collection元素的值
     * @param collection 实例
     * @param placeholder set方法名或索引名称
     * @return Object
     */
    private Object parseCollectionValue(Collection<?> collection, String placeholder) throws NoSuchMethodException {
        // 判断访问的是索引数据
        if (this.checkBracketed(placeholder)) {
            // 判断下表是否越界
            int collectionIndex = Integer.parseInt(this.parseBracketContent(placeholder));
            if (collectionIndex < 0 || collectionIndex >= collection.size()) {
                throw new IndexOutOfBoundsException("Collection索引下标越界:"+collectionIndex);
            }
            // 从缓存中获取collection的值
            Object[] setValues = collectionvaluemap.computeIfAbsent(collection, s -> {
                Object[] values = new Object[s.size()];
                int index = 0;
                for (Object value : s) {
                    values[index++] = value;
                }
                return values;
            });
            return setValues[collectionIndex];
        }
        // 返回调用的方法
        return this.parseOtherTypeValue(collection, placeholder);
    }


    /**
     * 读取数组中的值
     * @param obj 数组
     * @param placeholder 参数引用名称
     * @return Object
     * @throws ArrayIndexOutOfBoundsException 找不到指定索引时抛出此异常
     */
    private Object parseArrayValue(Object obj, String placeholder) {
        if ("length".equals(placeholder)) {
            return Array.getLength(obj);
        }
        if (!this.checkBracketed(placeholder)) {
            throw new IllegalArgumentException("不合法的数组索引:"+placeholder);
        }
        try {
            String indexString = this.parseBracketContent(placeholder);
            return Array.get(obj, Integer.parseInt(indexString));
        } catch (ArrayIndexOutOfBoundsException exception) {
            throw new ArrayIndexOutOfBoundsException("下标"+placeholder+"越界");
        }
    }


    /**
     * 提取方括号[]中的值
     * @param context context
     * @return String
     */
    private String parseBracketContent(String context) {
        return context.substring(1, context.length() - 1);
    }


    /**
     * 检查方括号是否合法
     * @param context context
     * @return boolean true合法 false不合法
     */
    private boolean checkBracketed(String context) {
        return context.startsWith("[") && context.endsWith("]");
    }


    /**
     * 判断实例是否是上下文
     * @param object obj
     * @return boolean
     */
    private boolean isContext(Object object) {
        return this.context == object;
    }


    /**
     * 解析Entity实体中的值
     * @param obj 被{@code Entity}注解的实体类
     * @param placeholder 参数引用名称
     * @return Object
     * @throws NoSuchMethodException 找不到指定字段时抛出此异常
     */
    private Object parseEntityValue(Object obj, String placeholder) throws NoSuchMethodException, NoSuchFieldException {
        try {
            FieldInfo fieldInfo = ReflectUtil.getColumnInfo(placeholder, obj);
            Object value = ReflectUtil.getValue(fieldInfo.getField(), obj);
            if (Objects.nonNull(fieldInfo.getTypeHandler())) {
                TypeHandler<?> typeHandler = fieldInfo.getTypeHandler();
                value = Parameter.wrap(value, typeHandler);
            }
            return value;
        } catch (NoSuchFieldException exception) {
            if (!this.isContext(obj)) {
                return this.parseOtherTypeValue(obj, placeholder);
            }
            throw exception;
        }
    }


    /**
     * 解析其他类型中的值
     * @param obj 任意类型
     * @param placeholder 参数引用名称
     * @return Object
     * @throws NoSuchMethodException 找不到指定方法时抛出此异常
     */
    private Object parseOtherTypeValue(Object obj, String placeholder) throws NoSuchMethodException {
        Class<?> paramEntityClass = obj.getClass();
        Method method = ReflectUtil.getMethod(paramEntityClass, placeholder);
        return ReflectUtil.invoke(method, obj);
    }



    /**
     * 如果给定的值是Parameter类型那么返回其中的value值
     * @param value val
     * @return Object
     */
    private Object unwrapValue(Object value) {
        return value instanceof Parameter ? ((Parameter)value).getValue() : value;
    }


    /**
     * 如果给定的值是一个枚举类型（Enum),则使用Parameter将其包装:如果不是枚举类型，则保持原样返回
     * @param value val
     * @return 如果value是枚举类型则使用Parameter包装并返回
     */
    private Object wrapValueIfEnum(Object value) {
        return value instanceof Enum ? Parameter.wrap(value, new EnumTypeHandler((Class<Enum<?>>) value.getClass())) : value;
    }


    /**
     * 将字符串根据 . 和 [] 分割，并将结果放入 LinkedList 中
     * @param expression 输入字符串
     * @return 分割后的元素列表
     */
    public static LinkedList<String> parseExpress(String expression) {
        LinkedList<String> expressionLinkedAccess = new LinkedList<>();
        if (StringUtil.isNoNull(expression)) {
            LinkedList<String> tmpExpressionLinked = expressionLinkedCache.computeIfAbsent(expression, express -> {
                LinkedList<String> result = new LinkedList<>();
                StringBuilder currentSegment = new StringBuilder();
                boolean inIndex = false;

                for (int i = 0; i < expression.length(); i++) {
                    char c = expression.charAt(i);

                    if (c == '[') {
                        // 开始处理索引部分
                        if (currentSegment.length() > 0) {
                            // 如果当前段非空，添加到结果中
                            result.add(currentSegment.toString());
                            currentSegment.setLength(0); // 清空当前段
                        }
                        currentSegment.append(c); // 添加 '[' 到当前段
                        inIndex = true;
                    } else if (c == ']') {
                        // 结束处理索引部分
                        if (inIndex) {
                            currentSegment.append(c); // 添加 ']' 到当前段
                            result.add(currentSegment.toString()); // 添加整个索引部分
                            currentSegment.setLength(0); // 清空当前段
                            inIndex = false;
                        }
                    } else if (c == '.') {
                        // 遇到点号，结束当前段
                        if (currentSegment.length() > 0) {
                            result.add(currentSegment.toString());
                            currentSegment.setLength(0); // 清空当前段
                        }
                    } else {
                        // 普通字符，追加到当前段
                        if (c != '(' && c != ')') {
                            currentSegment.append(c);
                        }
                    }
                }
                // 添加最后一个字段
                if (currentSegment.length() > 0) {
                    result.add(currentSegment.toString());
                }
                return result;
            });
            // 将缓存中的表达式copy至新的列表中
            expressionLinkedAccess.addAll(tmpExpressionLinked);
        }

        return expressionLinkedAccess;
    }
}
